﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Interactivity;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;

namespace nRoute.Behaviors.Interactivity
{
    public abstract class TargetedTriggerAction<T>
        : TriggerAction
        where
            T : FrameworkElement
    {
        public static readonly DependencyProperty TargetObjectProperty =
            DependencyProperty.Register("TargetObject", typeof(object), typeof(TargetedTriggerAction<T>),
            new PropertyMetadata(Utilities.Compatability.DP_UNSET_VALUE, new PropertyChangedCallback(OnTargetObjectChanged)));

        public static readonly DependencyProperty TargetNameProperty =
            DependencyProperty.Register("TargetName", typeof(string), typeof(TargetedTriggerAction<T>),
            new PropertyMetadata(Utilities.Compatability.DP_UNSET_VALUE, new PropertyChangedCallback(OnTargetNameChanged)));

        private T _target;

        [Category("Common Properties")]
        public Object TargetObject
        {
            get { return GetValue(TargetObjectProperty); }
            set { SetValue(TargetObjectProperty, value); }
        }

        [Category("Common Properties")]
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
        public Binding TargetObjectBinding
        {
            get { return GetBinding(TargetObjectProperty); }
            set { SetBinding<Object>(TargetObjectProperty, value); }
        }

        [Category("Common Properties")]
        public string TargetName
        {
            get { return Convert.ToString(GetValue(TargetNameProperty)); }
            set { SetValue(TargetNameProperty, value); }
        }

        [Category("Common Properties")]
        [CustomPropertyValueEditor(CustomPropertyValueEditor.PropertyBinding)]
        public Binding TargetNameBinding
        {
            get { return GetBinding(TargetNameProperty); }
            set { SetBinding<string>(TargetNameProperty, value); }
        }

        protected T Target
        {
            get { return _target; }            
        }

#region Overrides

        protected override void OnAttached()
        {
            base.OnAttached();
            if (_target == null)
            {
                _target = ResolveTarget();
                if (_target != null) OnTargetChanged(null, _target);
            }
        }

        protected override void OnDetaching()
        {
            _target = null;
            base.OnDetaching();
        }

        protected virtual void OnTargetChanged(T oldTarget, T newTarget) { }

#endregion

#region Handlers

        private static void OnTargetObjectChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((TargetedTriggerAction<T>)d).UpdateTarget((T)e.NewValue);
        }

        private static void OnTargetNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((TargetedTriggerAction<T>)d).UpdateTargetName(Convert.ToString(e.NewValue));
        }

        private void UpdateTarget(T newTarget)
        {
            if (_target != newTarget) OnTargetChanged(_target, newTarget);
            _target = newTarget;
        }

        private void UpdateTargetName(string newTargetName)
        {
            var _oldTarget = _target;
            _target = ResolveTarget();
            if (_oldTarget != _target) OnTargetChanged(_oldTarget, _target);
        }

#endregion
        
#region Helpers

        private T ResolveTarget()
        {
            if (this.TargetObject != null) return (T)this.TargetObject;

            if (!string.IsNullOrWhiteSpace(this.TargetName) && this.AssociatedObject != null && this.AssociatedObject is FrameworkElement)
            {
                var _target = ((FrameworkElement)base.AssociatedObject).FindName(this.TargetName);
                if (_target != null) return (T)_target;
            }

            // default to this
            return (T)base.AssociatedObject;
        }

#endregion

    }
}
